const { Connection, PublicKey } = require('@solana/web3.js');
const { Program, Idl } = require('@project-serum/anchor');
const axios = require('axios');
const path = require('path');
const fs = require('fs');
const PQueue = require('p-queue').default; // 使用 .default

// 创建并发队列
const queue = new PQueue({
  concurrency: 5, // 并发数
  timeout: 10000, // 10秒超时
  throwOnTimeout: true
});

// 验证 @solana/web3.js 版本
const web3Version = require('@solana/web3.js/package.json').version;
console.log(`Using @solana/web3.js version: ${web3Version}`);
if (web3Version !== '1.98.0') {
  console.warn(`Expected @solana/web3.js version 1.98.0, but found ${web3Version}`);
}

// 加载 dotenv 并处理错误
let dotenv;
try {
  dotenv = require('dotenv');
  const envPath = path.resolve(__dirname, '.env');
  if (!fs.existsSync(envPath)) {
    throw new Error(`.env file not found at ${envPath}`);
  }
  const envConfig = dotenv.config({ path: envPath });
  if (envConfig.error) {
    throw envConfig.error;
  }
  console.log('Successfully loaded .env file');
} catch (error) {
  console.error('Failed to load .env file:', error.message);
  console.error('Please create a .env file with SOLANA_RPC_URL in /Users/sange/js/encryptrun/pumpfun/.env');
  process.exit(1);
}

// 验证 SOLANA_RPC_URL
const RPC_URL = process.env.SOLANA_RPC_URL;
if (!RPC_URL) {
  console.error('SOLANA_RPC_URL is not defined in .env file');
  console.error('Example: SOLANA_RPC_URL=https://solana-mainnet.g.alchemy.com/v2/your_key');
  process.exit(1);
}
console.log(`Using RPC URL: ${RPC_URL}`);

// 初始化 Solana 连接
let connection;
try {
  connection = new Connection(RPC_URL, {
    commitment: 'confirmed',
    wsEndpoint: RPC_URL.replace('https', 'wss'),
  });
} catch (error) {
  console.error('Failed to initialize Solana connection:', error.message);
  process.exit(1);
}

// 测试 RPC 连接（使用 getVersion）
async function testRpcConnection() {
  try {
    const version = await queue.add(() => connection.getVersion());
    console.log(`RPC connection is healthy, Solana version: ${version['solana-core']}`);
  } catch (error) {
    console.error('RPC connection test failed:', error.message);
    console.error('Please verify SOLANA_RPC_URL in .env');
    process.exit(1);
  }
}

// 程序 ID 和迁移账户
const PUMP_FUN_PROGRAM = new PublicKey('6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P');
const RAYDIUM_AMM_PROGRAM = new PublicKey('675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8');
const METEORA_DLMM_PROGRAM = new PublicKey('DLMMqB6oT85csVw15Rw1N2hS3qK1m2b3nY5D9JRWNy8');
const PUMP_FUN_MIGRATION_ACCOUNT = new PublicKey('39azUYFWPz3VHgKCf3VChUwbpURdCHRxjWVowf5jUJjg');

// 验证公钥
function validatePublicKey(key, name) {
  try {
    new PublicKey(key);
    return true;
  } catch (error) {
    console.error(`Invalid public key for ${name}: ${key}`, error.message);
    return false;
  }
}

// 验证所有公钥
if (!validatePublicKey(PUMP_FUN_PROGRAM, 'PUMP_FUN_PROGRAM') ||
    !validatePublicKey(RAYDIUM_AMM_PROGRAM, 'RAYDIUM_AMM_PROGRAM') ||
    !validatePublicKey(METEORA_DLMM_PROGRAM, 'METEORA_DLMM_PROGRAM') ||
    !validatePublicKey(PUMP_FUN_MIGRATION_ACCOUNT, 'PUMP_FUN_MIGRATION_ACCOUNT')) {
  process.exit(1);
}

// [保持原有的 PUMP_FUN_IDL 和 RAYDIUM_AMM_IDL 定义不变...]
const PUMP_FUN_IDL = require('./pump_fun_idl.json'); // 调整路径
const RAYDIUM_AMM_IDL = require('./raydium_amm_idl.json'); // 调整路径
// 初始化程序
const pumpFunProgram = new Program(PUMP_FUN_IDL, PUMP_FUN_PROGRAM, { connection });
const raydiumProgram = new Program(RAYDIUM_AMM_IDL, RAYDIUM_AMM_PROGRAM, { connection });

// 获取 SOL 价格
async function getSolPrice() {
  try {
    const response = await queue.add(() => 
      axios.get('https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd')
    );
    return response.data.solana.usd;
  } catch (error) {
    console.error('Failed to fetch SOL price:', error.message);
    return 150; // 默认值
  }
}

// 缓存 Bonding Curve 映射
const BONDING_CURVE_CACHE = path.resolve(__dirname, 'bonding_curve_cache.json');
function loadBondingCurveCache() {
  try {
    if (fs.existsSync(BONDING_CURVE_CACHE)) {
      const data = JSON.parse(fs.readFileSync(BONDING_CURVE_CACHE, 'utf8'));
      return new Map(data);
    }
  } catch (error) {
    console.error('Failed to load bonding curve cache:', error.message);
  }
  return new Map();
}

function saveBondingCurveCache(map) {
  try {
    fs.writeFileSync(BONDING_CURVE_CACHE, JSON.stringify([...map], null, 2));
  } catch (error) {
    console.error('Failed to save bonding curve cache:', error.message);
  }
}

// 获取 Bonding Curve 数据
async function getBondingCurveData(mintAddress) {
  try {
    // 通过 mint 查询 Bonding Curve
    const bondingCurveAddress = Array.from(bondingCurveToMint.entries())
      .find(([_, mint]) => mint === mintAddress)?.[0];
    if (!bondingCurveAddress) {
      console.warn(`No Bonding Curve found for mint: ${mintAddress}`);
      return null;
    }

    const bondingCurve = await queue.add(() => 
      pumpFunProgram.account.bondingCurve.fetch(new PublicKey(bondingCurveAddress))
    );
    
    if (!bondingCurve.complete) {
      console.warn(`Bonding Curve not complete for mint: ${mintAddress}`);
      return null;
    }

    const solPrice = await getSolPrice();
    const price = (Number(bondingCurve.virtualSolReserves) / Number(bondingCurve.virtualTokenReserves)) * solPrice;
    const marketCap = price * 1e9;

    return { price, marketCap, bondingCurveAddress };
  } catch (error) {
    console.error(`Failed to fetch Bonding Curve for mint ${mintAddress}:`, error.message);
    return null;
  }
}

// 获取 Meteora 池地址
async function getMeteoraPoolAddress(mintAddress) {
  try {
    const accounts = await queue.add(() => 
      connection.getProgramAccounts(METEORA_DLMM_PROGRAM, {
        filters: [
          { dataSize: 1024 },
          { memcmp: { offset: 32, bytes: mintAddress } }
        ]
      })
    );
    return accounts.length > 0 ? { address: accounts[0].pubkey.toBase58(), type: 'DLMM' } : null;
  } catch (error) {
    console.error(`Failed to fetch Meteora pool for mint ${mintAddress}:`, error.message);
    return null;
  }
}

// 存储 Bonding Curve 和 mint 映射
const bondingCurveToMint = loadBondingCurveCache();

// 指数退避重试
async function withRetry(fn, maxRetries = 3, baseDelay = 500) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await queue.add(fn);
    } catch (error) {
      if (error.message.includes('429') && i < maxRetries - 1) {
        const delay = baseDelay * Math.pow(2, i);
        console.log(`Server responded with 429 Too Many Requests. Retrying after ${delay}ms delay...`);
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error;
      }
    }
  }
}

// 监听 Pump.fun CreateEvent 建立 mint 映射
async function monitorCreateEvents() {
  console.log('Monitoring Pump.fun CreateEvents for mint mapping...');
  let subscriptionId = null;
  let retryCount = 0;
  const maxRetries = 5;

  const subscribe = async () => {
    try {
      subscriptionId = connection.onLogs(
        PUMP_FUN_PROGRAM,
        async (logs) => {
          try {
            const parsedLogs = logs.logs;
            for (const log of parsedLogs) {
              if (log.includes('Program log: CreateEvent')) {
                const event = pumpFunProgram.coder.events.decode('CreateEvent', log);
                if (event) {
                  bondingCurveToMint.set(event.data.bondingCurve.toBase58(), event.data.mint.toBase58());
                  saveBondingCurveCache(bondingCurveToMint);
                  console.log(`CreateEvent: BondingCurve=${event.data.bondingCurve}, Mint=${event.data.mint}`);
                }
              }
            }
          } catch (error) {
            console.error('Failed to parse CreateEvent:', error.message);
          }
        },
        'confirmed'
      );
      console.log('Subscribed to Pump.fun CreateEvents, subscription ID:', subscriptionId);
      retryCount = 0;
    } catch (error) {
      console.error('Failed to subscribe to Pump.fun CreateEvents:', error.message);
      if (retryCount < maxRetries) {
        retryCount++;
        console.log(`Retrying subscription (${retryCount}/${maxRetries})...`);
        if (subscriptionId) {
          await connection.removeOnLogsListener(subscriptionId);
          subscriptionId = null;
        }
        setTimeout(subscribe, 5000);
      } else {
        console.error('Max retries reached for Pump.fun CreateEvents subscription');
        process.exit(1);
      }
    }
  };

  await subscribe();
}

// 监听 Raydium 池子创建
async function subscribeRaydiumPoolCreation() {
  console.log('Subscribing to Raydium pool creations...');
  let subscriptionId = null;
  let retryCount = 0;
  const maxRetries = 5;

  const subscribe = async () => {
    try {
      subscriptionId = connection.onLogs(
        RAYDIUM_AMM_PROGRAM,
        async (logs) => {
          try {
          
      
            if (!logs.logs.some(log => log.includes('Instruction: initialize') || log.includes('Instruction: initialize2'))) {
              return;
            }

              // console.log('Received logs:',JSON.stringify(logs, null, 2));
            // 格式化日志并写入文件
            const logMessage = `Received logs:\n${JSON.stringify(logs, null, 2)}\n\n`;

            // 写入文件（追加模式）
            fs.appendFile('debug.log', logMessage, (err) => {
              // if (err) {
              //   console.error('Error writing to debug.log:', err);
              // } else {
              //   console.log('Logs written to debug.log');
              // }
            });

            const signature = logs.signature;
            const tx = await withRetry(() => 
              queue.add(() => 
                connection.getParsedTransaction(signature, { maxSupportedTransactionVersion: 0 })
              )
            );
            
            if (!tx) {
              console.warn(`Transaction not found for signature: ${signature}`);
              return;
            }

            const signer = tx.transaction.message.accountKeys.find(key => key.signer);
            if (!signer || !signer.pubkey.equals(PUMP_FUN_MIGRATION_ACCOUNT)) {
              console.log(`Signer is not Pump.fun migration account for signature: ${signature}`);
              return;
            }

            let isPoolCreation = false;
            let instructionName = '';
            const instructions = tx.transaction.message.instructions;
            for (const ix of instructions) {
              if (ix.programId.equals(RAYDIUM_AMM_PROGRAM)) {
                const parsedIx = raydiumProgram.coder.instruction.decode(ix.data, 'base58');
                if (parsedIx && (parsedIx.name === 'initialize' || parsedIx.name === 'initialize2')) {
                  isPoolCreation = true;
                  instructionName = parsedIx.name;
                  break;
                }
              }
            }

            if (!isPoolCreation) {
              console.log(`No pool creation instruction found for signature: ${signature}`);
              return;
            }

            console.log(`Detected Raydium pool creation instruction: ${instructionName}, signature: ${signature}`);

            const ammAccountKey = tx.transaction.message.accountKeys.find(key =>
              key.writable && !key.signer && key.pubkey.toBase58() !== PUMP_FUN_MIGRATION_ACCOUNT.toBase58()
            );
            if (!ammAccountKey) {
              console.warn(`No writable Amm account found for signature: ${signature}`);
              return;
            }

            const ammAccount = await queue.add(() => 
              raydiumProgram.account.ammInfo.fetch(ammAccountKey.pubkey)
            );
            const mint = ammAccount.coinMint.toBase58();
            const poolType = ammAccount.status.toNumber() === 0 ? 'CPMM' : 'CLMM';

            const tokenData = await getBondingCurveData(mint);
            if (!tokenData) {
              console.warn(`No valid Bonding Curve data for mint: ${mint}`);
              return;
            }

            const { price, marketCap, bondingCurveAddress } = tokenData;
            if (marketCap < 60000 || marketCap > 80000) {
              console.log(`Market cap ${marketCap.toFixed(2)} out of range for mint: ${mint}`);
              return;
            }

            const pools = [{ address: ammAccountKey.pubkey.toBase58(), type: `AMM (${poolType})` }];
            const meteoraPool = await getMeteoraPoolAddress(mint);
            if (meteoraPool) pools.push(meteoraPool);

            console.log(`Detected Pump.fun migrated token:
  Token Mint: ${mint}
  Price: $${price.toFixed(6)}
  Market Cap: $${marketCap.toFixed(2)}
  Bonding Curve: ${bondingCurveAddress}
  Pools: ${JSON.stringify(pools, null, 2)}
  Migration Time: ${new Date(tx.blockTime * 1000).toISOString()}`);
          } catch (error) {
            console.error(`Failed to process Raydium pool creation for signature ${logs.signature}:`, error.message);
          }
        },
        'confirmed'
      );
      console.log('Subscribed to Raydium pool creations, subscription ID:', subscriptionId);
      retryCount = 0;
    } catch (error) {
      console.error('Failed to subscribe to Raydium pool creations:', error.message);
      if (retryCount < maxRetries) {
        retryCount++;
        console.log(`Retrying subscription (${retryCount}/${maxRetries})...`);
        if (subscriptionId) {
          await connection.removeOnLogsListener(subscriptionId);
          subscriptionId = null;
        }
        setTimeout(subscribe, 5000);
      } else {
        console.error('Max retries reached for Raydium pool creations subscription');
        process.exit(1);
      }
    }
  };

  await subscribe();
}

// 主函数
async function main() {
  await testRpcConnection();
  try {
    await Promise.all([monitorCreateEvents(), subscribeRaydiumPoolCreation()]);
  } catch (error) {
    console.error('Subscription failed:', error.message);
    process.exit(1);
  }
}

// 启动程序
main().catch(error => {
  console.error('Fatal error in main:', error.message);
  process.exit(1);
});